package com.kaikeba.estack.web.mvc;

import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 映射器（包含了大量的网址与方法的对应关系）
 */
public class HandlerMapping {

    private static final Logger logger = LoggerFactory.getLogger(HandlerMapping.class);

    private static Map<String, MVCMapping> data = new HashMap<>();

    public static MVCMapping get(String uri) {
        return data.get(uri);
    }


    public static void load(String packageName) throws IOException {
        ImmutableSet<ClassPath.ClassInfo> controllerClasses = ClassPath.from(HandlerMapping.class.getClassLoader()).getTopLevelClassesRecursive(packageName);

        controllerClasses.forEach(classInfo -> {
            loadController(classInfo.load());
        });

        //获取配置文件中描述的一个个的类
    }


    private static void loadController(Class<?> clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        boolean controllerResult = Arrays.stream(clazz.getAnnotations()).anyMatch(annotation -> annotation instanceof Controller);
        //未被controller标注的不进行处理
        if (!controllerResult) {
            return;
        }

        Optional<Annotation> classRequestMapping = Arrays.stream(annotations)
                .filter(annotation -> annotation instanceof RequestMapping).findAny();
        String clazzRequestLocation = classRequestMapping.map(annotation -> {
            String value = ((RequestMapping) annotation).value();
            value = handlerLocation(value);
            if (value == null) return StringUtils.EMPTY;
            return value;
        }).orElse(StringUtils.EMPTY);

        //创建这个类的对象
        Object controllerInstance = null;
        try {
            controllerInstance = clazz.getConstructor().newInstance();
        } catch (Exception e) {
            logger.error("初始化controller:[{}]失败", clazz, e);
            return;
        }
        //获取这个类的所有方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            Annotation[] as = method.getAnnotations();
            if (as != null) {
                for (Annotation annotation : as) {
                    if (annotation instanceof RequestMapping) {
                        RequestMapping methodRequestMapping = (RequestMapping) annotation;
                        ResponseType responseType = methodRequestMapping.responseType();
                        String location = clazzRequestLocation + handlerLocation(methodRequestMapping.value());
                        //当函数上没有设置responseType时，使用类的responseType，如果类上也没有时，默认为json
                        if (ResponseType.NONE.equals(responseType)) {
                            responseType = classRequestMapping
                                    .map(classAnnotation -> {
                                        ResponseType classResponseType = ((RequestMapping) classAnnotation).responseType();
                                        return ResponseType.NONE.equals(classResponseType) ? ResponseType.JSON : classResponseType;
                                    }).orElse(ResponseType.JSON);
                        }
                        //说明此方法，用于返回字符串给客户端
                        recordMapping(controllerInstance, method, responseType, location);
                    }
                }
            }
        }
    }

    private static void recordMapping(Object controllerInstance, Method method, ResponseType responseType, String location) {
        logger.info("注册[{}]到controller[{}]", location, controllerInstance.getClass().getName() + "." + method.getName());
        MVCMapping mapping = new MVCMapping(controllerInstance, method, responseType);
        Object o = data.put("/backend" + location, mapping);
        if (o != null) {
            //存在了重复的请求地址
            throw new RuntimeException("请求地址重复：" + location);
        }
    }

    /**
     *
     * @param value
     * @return  空字符串   /a  /b   /a/b
     */
    private static String handlerLocation(String value) {
        if (StringUtils.equals(value, "/")) {
            return StringUtils.EMPTY;
        }

        //@RequestMapping("hello")
        if (!StringUtils.startsWith(value, "/")) {
            value = "/" + value;
        }
        if (StringUtils.endsWith(value, "/")) {
            value = StringUtils.substring(value, 0, value.length() - 1);
        }
        return value;
    }

    /**
     * 映射对象，每一个对象封装了一个方法，用于处理请求
     */
    public static class MVCMapping {
        private Object obj;
        private Method method;
        private ResponseType responseType;

        public MVCMapping() {
        }

        public MVCMapping(Object obj, Method method, ResponseType responseType) {
            this.obj = obj;
            this.method = method;
            this.responseType = responseType;
        }

        public Object getObj() {
            return obj;
        }

        public void setObj(Object obj) {
            this.obj = obj;
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public ResponseType getResponseType() {
            return responseType;
        }

        public void setResponseType(ResponseType responseType) {
            this.responseType = responseType;
        }
    }
}
